let log = console.log;

function noop() {
}

function isFunction(fn) {
    return typeof fn == 'function';
}

function isArray(any) {
    return Array.isArray(any);
}

//window.onload
//document.onload
//document.documentElement.onload
function iframeReady(contentWindow, callback) {
    let iid;
    iid = setInterval(function () {
        if (contentWindow.document.readyState == 'complete') {
            clearInterval(iid);
            callback();
        }
    }, 1);
}

function documentReady(callback) {
    let iid;
    iid = setInterval(function () {
        if (document.readyState == 'complete') {
            clearInterval(iid);
            callback();
        }
    }, 1);
}

function j2s(obj) {
    return JSON.stringify(obj);
}

function s2j(str) {
    return JSON.parse(str);
}

function dw(s) {
    document.write(s);
}

function dt(s) {
    document.title = s;
}

function dce(tagName) {
    return document.createElement(tagName);
}

function qs(element, selector) {
    return element.querySelector(selector);
}

function qsa(element, selector) {
    return element.querySelectorAll(selector);
}

//https://www.w3school.com.cn/jsref/dom_obj_event.asp
let dom_obj_event = {
    wheel: 'wheel',
    click: 'click',
    dblclick: 'dblclick',
    resize: 'resize',
    change: 'change',
    dragenter: 'dragenter',
    dragover: 'dragover',
    dragleave: 'dragleave',
    drop: 'drop',
    mouseover: 'mouseover',
    mouseenter: 'mouseenter',
    mousemove: 'mousemove',
    mouseout: 'mouseout',
    mouseleave: 'mouseleave',
    mousedown: 'mousedown',
    mouseup: 'mouseup'
};

// JS event.button 鼠标事件发生时按下的鼠标按钮-数字值：
// 鼠标左键 0
// 滚轮按钮或中间按钮 1
// 鼠标右键 2
let event_button = {
    left: 0,
    middle: 1,
    right: 2
};

// JS event.buttons 触发鼠标事件时按下了哪个或哪些鼠标按钮
let event_buttons = {
    left: 1,
    middle: 4,
    right: 2,
    LM: 5,
    LR: 3,
    MR: 6,
    LMR: 7
};//注意：缺乏浏览器支持

// 鼠标左键
function mouseLeft(event) {
    return event.button == event_button.left;
}

// 滚轮按钮或中间按钮
function mouseMiddle(event) {
    return event.button == event_button.middle;
}

// 鼠标右键
function mouseRight(event) {
    return event.button == event_button.right;
}

function ae(element, type, fn, capture) {
    element.addEventListener(type, fn);
}

function re(element, type, fn, capture) {
    element.removeEventListener(type, fn);
}

function de(element, type) {
    element.dispatchEvent(new CustomEvent(type));
}

function addClass(element, clazz) {
    element.classList.add(clazz);
}

function removeClass(element, clazz) {
    element.classList.remove(clazz);
}

function hasClass(element, clazz) {
    return element.classList.contains(clazz);
}

function toggleClass(element, clazz) {
    if (hasClass(element, clazz)) {
        removeClass(element, clazz);
    } else {
        addClass(element, clazz);
    }
}

function setAttr(element, name, value) {
    element.setAttribute(name, value);
}

function getAttr(element, name) {
    return element.getAttribute(name);
}

function removeAttr(element, name) {
    return element.removeAttribute(name);
}

function hasAttr(element, name) {
    return element.hasAttribute(name);
}

function isVisible(element) {
    return element.style.visibility == 'visible';
}

function isHidden(element) {
    return element.style.visibility == 'hidden';
}

function setVisibility(element, value) {
    if (typeof value == 'boolean') {
        element.style.visibility = value ? 'visible' : 'hidden';
    } else {
        element.style.visibility = value;
    }
}

function setDisplay(element, value) {
    if (typeof value == 'boolean') {
        element.style.display = value ? '' : 'none';
    } else {
        element.style.display = value;
    }
}

function show(element) {
    setVisibility(element, true);
    setDisplay(element, true);
}

function hide(element) {
    setVisibility(element, false);
    setDisplay(element, false);
}

function setBackgroundImage(element, src) {
    if (src == undefined || src == '' || src == 'none') {
        element.style.backgroundImage = 'none';
    } else {
        element.style.backgroundImage = 'url("' + src + '")';
    }
}

function setBackgroundColor(element, color) {
    element.style.backgroundColor = color;
}

function loadImage(src, callback) {
    let img = dce('img');
    img.onload = function () {
        callback(this, this.src, this.naturalWidth, this.naturalHeight);
    };
    img.crossOrigin = '';//fix insecure
    img.src = src;
}

function buildBase64FromImage(img, callback) {
    let src;
    if (typeof img == 'object') {
        src = img.src;
    } else if (typeof img == 'string') {
        src = img;
    }
    loadImage(src, (img, src, width, height) => {
        let canvas = dce('canvas');
        canvas.width = width;
        canvas.height = height;
        let context = canvas.getContext('2d');
        context.drawImage(img, 0, 0, width, height);
        let dataURL = canvas.toDataURL();//'image/jpeg',1
        callback(dataURL);//...
    });
}

function setDimension(element, dimension) {
    element.style.width = dimension.width + 'px';
    element.style.height = dimension.height + 'px';
}

function setBackgroundPosition(element, position) {
    element.style.backgroundPositionX = position.x + 'px';
    element.style.backgroundPositionY = position.y + 'px';
}

function setBackgroundSize(element, width, height) {
    element.style.backgroundSize = width + 'px ' + height + 'px';
}

function setPositioning(element, position) {
    element.style.left = position.x + 'px';
    element.style.top = position.y + 'px';
}

function numberOfPixel(ps) {
    return Number(ps.substring(0, ps.length - 2));
}

//formatString("%5s% 03n",'Jack', 23);
//[ Jack 23]
function formatString() {
    log(arguments);
    if (arguments.length) {
    }
}

function apply_previousElementSibling() {
    //previousElementSibling
}

function newCSS() {
    let css = new CSSStyleSheet();
    css.insertRule("#blanc { color: white }", 0);
    css.deleteRule(0);
}

function newSimpleAnimation(element, keyframes, options, onfinish, oncancel, onremove) {
    let ao = element.animate(keyframes, options);//Animation
    if (isFunction(onfinish)) {
        ao.onfinish = onfinish;
    }
    if (isFunction(oncancel)) {
        ao.oncancel = oncancel;
    }
    if (isFunction(onremove)) {
        ao.onremove = onremove;
    }
    return {
        play: () => ao.play(),
        pause: () => ao.pause(),
        stop: () => ao.finish(),
        cancel: () => ao.cancel(),
        reverse: () => ao.reverse(),
        getAnimation: () => ao,
        getKeyframeEffect: () => ao.effect,
        setKeyframeEffect: (keyframes, options) => ao.effect = new KeyframeEffect(element, keyframes, options),
        getKeyframes: () => ao.effect.getKeyframes(),
        setKeyframes: (keyframes) => ao.effect.setKeyframes(keyframes)
    };
}

function getBeginOfMonth(date) {
    if (date == undefined) {
        date = new Date();
    }
    date.setDate(1);
    return getDateStr(date);
}

function getEndOfMonth(date) {
    if (date == undefined) {
        date = new Date();
    }
    date.setDate(1);
    date.setMonth(date.getMonth() + 1);
    date.setDate(0);
    return getDateStr(date);
}

function getDateStr(date) {
    if (date == undefined) {
        date = new Date();
    }
    let ny = date.getFullYear();
    let nm = date.getMonth() + 1;
    let nd = date.getDate();
    //TODO: replace with zeroPadding
    return [ny, nm > 9 ? nm : '0' + nm, nd > 9 ? nd : '0' + nd].join('-');
}

function getTimeStr(date) {
    if (date == undefined) {
        date = new Date();
    }
    let nh = date.getHours();
    let nm = date.getMinutes();
    let ns = date.getSeconds();
    let nms = date.getMilliseconds();
    //TODO: replace with zeroPadding
    return [nh > 9 ? nh : '0' + nh, nm > 9 ? nm : '0' + nm, ns > 9 ? ns : '0' + ns].join(':') + '.' + (nms > 99 ? nms : (nms > 9 ? '0' + nms : '00' + nms));
}

function datetime(date) {
    return getDateStr(date) + ' ' + getTimeStr(date);
}

//int 3 -> 0,1,2
function randomIndex(size) {
    return Math.floor(Math.random() * Math.pow(10, String(size).length)) % size;
}

function getDocDimension() {
    let doc = document.documentElement;
    return {
        width: doc.clientWidth,
        height: doc.clientHeight
    };
}

function getDOMRect(element) {
    return element.getBoundingClientRect();
}

//Params
//e.g., getParams(location.search)
function getParams(url) {
    let wls = location.search;// e.g., ?a=1&b=2&b=3
    if (wls != '') {
        let res = {};
        let params_arr = wls.substring(1).split('&');
        params_arr.forEach((params) => {
            let kvp_arr = params.split('=');
            let key = kvp_arr[0];
            let value = kvp_arr[1];
            if (res[key] == undefined) {
                res[key] = [];
            }
            res[key].push(value);
        });
        return res;
    }
    return undefined;
}

//get value by name
//you can define let params = getParams(), then call this fn
function getQueryString(params, name) {
    if (params != undefined) {
        return params[name];
    }
    return undefined;
}

function clone(source) {
    if ('undefined/boolean/number/string/function'.indexOf(typeof source) > -1) {
        return source;
    }
    if (typeof source == 'object') {
        if (source === null) {
            return null;
        }
        if (isArray(source)) {
            let array = new Array();
            source.forEach(item => array.push(clone(item)));
            return array;
        }
        let object = new Object();
        for (let key in source) {
            object[key] = clone(source[key]);
        }
        return object;
    }
    throw 'unknown type';
}

function contains(collection, element) {
    for (let item of collection) {
        if (item === element) {
            return true;
        }
    }
    return false;
}

function sendPostRequest(url, data, success, error) {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = () => {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                if (isFunction(success)) {
                    success(xhr.responseText);
                }
            } else if (isFunction(error)) {
                error(xhr.statusText + '(' + xhr.status + '): ' + xhr.responseText);
            }
        }
    };
    xhr.send(j2s(data));
    //XMLHttpRequestUpload
}

function humanSize(bytes) {
    let labels = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
    let base = 1024;
    let exponent = 0;
    let size = bytes;
    while (true) {
        if (exponent == labels.length - 1 || (size = bytes / Math.pow(base, exponent)) < base) {
            break;
        }
        exponent++;
    }
    return size.toFixed(2) + ' ' + labels[exponent];
}

function humanTime(ms) {
    let labels = ['毫秒', '秒', '分', '时', '天'];
    // let dd=new Date;
    // dd.getMilliseconds();
    // dd.getSeconds();
    // dd.getMinutes();
    // dd.getHours();
    // dd.getFullYear();
    // dd.getMonth();
    // dd.getDate();

    let baseArr = [1, 1000, 60000, 3600000, 86400000];
    let exponent = 0;
    let size = ms;
    while (true) {
        if (exponent == labels.length - 1 || (size = ms / baseArr[exponent]) < 1e3) {
            break;
        }
        exponent++;
    }
    return size.toFixed(2) + ' ' + labels[exponent];
}

//see also: open
function goto(url) {
    window.location.href = url;
}